﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using Assist;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using YArchitech.Revit;
using YJKArchMethodLibrary.Utils;
using YJKView;

namespace WallCreateAndEdit.WallModule.WallAlignmentToBeam
{
	[Regeneration(RegenerationOption.Manual)]
	[Transaction(TransactionMode.Manual)]
	public class CmdWallAlignmentToBeam : IExternalCommand
	{
		public Result Execute(ExternalCommandData cmdData, ref string msg, ElementSet elems)
		{
			this.m_Revit = cmdData;
			this.m_Doc = this.m_Revit.Application.ActiveUIDocument.Document;
			Result result;
			try
			{
				if (cmdData == null)
				{
					throw new System.ArgumentNullException("commandData");
				}
				CmdViewType types = (CmdViewType)11;
				if (SwitchView.SetUsableView(types, cmdData) != Autodesk.Revit.UI.Result.Succeeded)
				{
					result = Autodesk.Revit.UI.Result.Failed;
				}
				else
				{
					ElementId oldBaselineValue = null;
					this.HideBaseline(ref oldBaselineValue);
					List<Wall> list = new List<Wall>();
					if (!this.SelectWalls(ref list))
					{
						this.UnHideBaseLine(oldBaselineValue);
						result = Autodesk.Revit.UI.Result.Cancelled;
					}
					else
					{
						this.UnHideBaseLine(oldBaselineValue);
						foreach (Wall wall in list)
						{
							Transaction transaction = new Transaction(this.m_Doc, "ExtendWallTemp");
							try
							{
								transaction.Start();
								ElementId elementId = wall.get_Parameter(BuiltInParameter.WALL_HEIGHT_TYPE).AsElementId();
								if (elementId == ElementId.InvalidElementId)
								{
									double num = wall.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM).AsDouble();
									wall.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM).Set(num + this.m_TopOffset);
								}
								else
								{
									double num2 = wall.get_Parameter(BuiltInParameter.WALL_TOP_OFFSET).AsDouble();
									wall.get_Parameter(BuiltInParameter.WALL_TOP_OFFSET).Set(num2 + this.m_TopOffset);
								}
								this.m_Doc.Regenerate();
								FilteredElementCollector filteredElementCollector = new FilteredElementCollector(this.m_Doc);
								filteredElementCollector.OfClass(typeof(FamilyInstance));
								filteredElementCollector.OfCategory(BuiltInCategory.OST_StructuralFraming);
								FilteredElementCollector filteredElementCollector2 = new FilteredElementCollector(this.m_Doc);
								filteredElementCollector2.OfClass(typeof(Floor));
								filteredElementCollector.UnionWith(filteredElementCollector2);
								ElementIntersectsElementFilter elementIntersectsElementFilter = new ElementIntersectsElementFilter(wall);
								filteredElementCollector.WherePasses(elementIntersectsElementFilter);
								IList<Element> list2 = filteredElementCollector.ToElements();
								transaction.RollBack();
								if (list2.Count >= 1)
								{
									double beamMaxTopElev = this.GetBeamMaxTopElev(list2);
									Transaction transaction2 = new Transaction(this.m_Doc, "ExtendWall");
									try
									{
										transaction2.Start();
										if (elementId == ElementId.InvalidElementId)
										{
											wall.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM).Set(beamMaxTopElev);
										}
										else
										{
											Level level = this.m_Doc.GetElement(elementId) as Level;
											wall.get_Parameter(BuiltInParameter.WALL_TOP_OFFSET).Set(beamMaxTopElev - level.Elevation);
										}
										transaction2.Commit();
									}
									catch
									{
										transaction2.RollBack();
									}
									Transaction transaction3 = new Transaction(this.m_Doc, "JoinOper");
									try
									{
										transaction3.Start();
										foreach (Element column in list2)
										{
											this.JoinTwoElements(column, wall);
										}
										transaction3.Commit();
									}
									catch
									{
										transaction3.RollBack();
									}
								}
							}
							catch (Exception)
							{
								transaction.RollBack();
								return Autodesk.Revit.UI.Result.Failed;
							}
						}
						result = Autodesk.Revit.UI.Result.Succeeded;
					}
				}
			}
			catch
			{
				result = Autodesk.Revit.UI.Result.Failed;
			}
			return result;
		}

		private void HideBaseline(ref ElementId oldBaselineValue)
		{
			Transaction transaction = null;
			try
			{
				transaction = new Transaction(this.m_Doc);
				transaction.Start("HideBaseline");
				Parameter underLayParameter = (this.m_Revit.Application.ActiveUIDocument.ActiveView as ViewPlan).GetUnderLayParameter();
				ElementId elementId = underLayParameter.AsElementId();
				oldBaselineValue = elementId;
				underLayParameter.Set(ElementId.InvalidElementId);
				transaction.Commit();
			}
			catch (Exception)
			{
				if (transaction != null)
				{
					transaction.RollBack();
				}
			}
		}

		private void UnHideBaseLine(ElementId oldBaselineValue)
		{
			Transaction transaction = null;
			try
			{
				transaction = new Transaction(this.m_Doc);
				transaction.Start("UnHideBaseline");
				Parameter underLayParameter = (this.m_Revit.Application.ActiveUIDocument.ActiveView as ViewPlan).GetUnderLayParameter();
				underLayParameter.Set(oldBaselineValue);
				transaction.Commit();
			}
			catch (Exception)
			{
				if (transaction != null)
				{
					transaction.RollBack();
				}
			}
		}

		private bool SelectWalls(ref List<Wall> walls)
		{
			IList<Reference> list = null;
			Selection selection = this.m_Revit.Application.ActiveUIDocument.Selection;
			try
			{
				list = selection.PickObjects(Autodesk.Revit.UI.Selection.ObjectType.Element, new WallCreateAndEdit.WallModule.WallFilter(), "选择墙");
			}
			catch (Exception)
			{
				return false;
			}
			int num = 0;
			new List<Curve>();
			foreach (Reference reference in list)
			{
				Element element = this.m_Doc.GetElement(reference);
				Wall wall = element as Wall;
				if (wall != null)
				{
					int num2 = wall.get_Parameter(BuiltInParameter.WALL_TOP_IS_ATTACHED).AsInteger();
					if (num2 == 1)
					{
						num++;
					}
					else
					{
						walls.Add(wall);
					}
				}
			}
			if (num > 0)
			{
				MessageBox.Show("有" + num.ToString() + "个依附着顶部的墙未进行处理", "盈建科提示");
			}
			return walls.Count != 0;
		}

		private double GetBeamMaxTopElev(IList<Element> collection)
		{
			double result = 0.0;
			bool flag = false;
			double num = double.MinValue;
			foreach (Element element in collection)
			{
				if (element.GetType() == typeof(Floor))
				{
					Floor floor = element as Floor;
					double num2 = floor.get_Parameter(BuiltInParameter.FLOOR_HEIGHTABOVELEVEL_PARAM).AsDouble();
					Parameter parameter = floor.get_Parameter(BuiltInParameter.LEVEL_PARAM);
					Level level = this.m_Doc.GetElement(parameter.AsElementId()) as Level;
					double num3 = level.Elevation + num2;
					flag = true;
					if (!Geometry.GreaterThan(num, num3))
					{
						num = num3;
					}
				}
				else if (!(element.GetType() != typeof(FamilyInstance)))
				{
					double val = element.get_Parameter(BuiltInParameter.STRUCTURAL_BEAM_END0_ELEVATION).AsDouble();
					double val2 = element.get_Parameter(BuiltInParameter.STRUCTURAL_BEAM_END1_ELEVATION).AsDouble();
					if (Geometry.IsEqual(val, val2))
					{
						double beamTopElevByFace = this.GetBeamTopElevByFace(element as FamilyInstance);
						flag = true;
						if (!Geometry.GreaterThan(num, beamTopElevByFace))
						{
							num = beamTopElevByFace;
						}
					}
				}
			}
			if (flag)
			{
				result = num;
			}
			return result;
		}

		private double GetBeamHeight(FamilyInstance beam)
		{
			double result;
			try
			{
				Options options = this.m_Revit.Application.Application.Create.NewGeometryOptions();
                options.DetailLevel = (ViewDetailLevel)3;
				options.ComputeReferences = true;
				List<Solid> list = new List<Solid>();
				GeometryElement geometryElement = beam.get_Geometry(options);
				if (null == geometryElement)
				{
					result = 0;
				}
				else
				{
					List<double> list2 = new List<double>();
					List<GeometryObject> geometryObjectArray = RevitVersionFuncs.GetGeometryObjectArray(geometryElement);
					foreach (GeometryObject geometryObject in geometryObjectArray)
					{
						if (geometryObject.GetType() == typeof(Solid))
						{
							Solid solid = geometryObject as Solid;
							if (solid != null && Geometry.GreaterThan(solid.Volume, 0.0))
							{
								list.Add(solid);
								continue;
							}
						}
						GeometryInstance geometryInstance = geometryObject as GeometryInstance;
						if (!(null == geometryInstance))
						{
							GeometryElement instanceGeometry = geometryInstance.GetInstanceGeometry();
							List<GeometryObject> geometryObjectArray2 = RevitVersionFuncs.GetGeometryObjectArray(instanceGeometry);
							foreach (GeometryObject geometryObject2 in geometryObjectArray2)
							{
								Solid solid2 = geometryObject2 as Solid;
								if (solid2 != null && Geometry.GreaterThan(solid2.Volume, 0.0))
								{
									list.Add(solid2);
								}
							}
						}
					}
					foreach (Solid solid3 in list)
					{
						foreach (object obj in solid3.Faces)
						{
							Face face = (Face)obj;
							XYZ xyz = face.ComputeNormal(new UV(0.0, 0.0));
							if (!xyz.IsAlmostEqualTo(XYZ.BasisZ))
							{
								if (!xyz.IsAlmostEqualTo(-XYZ.BasisZ))
								{
									continue;
								}
							}
							try
							{
                                Edge edge = face.EdgeLoops.get_Item(0).get_Item(0);
								IntersectionResult intersectionResult = face.Project(edge.AsCurve().GetEndPoint(0));
								list2.Add(intersectionResult.XYZPoint.Z);
							}
							catch (Exception)
							{
							}
						}
					}
					if (list2.Count < 2)
					{
						result = 0;
					}
					else
					{
						list2.Sort();
						result = Math.Abs(list2[list2.Count - 1] - list2[0]);
					}
				}
			}
			catch (Exception)
			{
				result = 0;
			}
			return result;
		}

		private double GetBeamTopElevByFace(FamilyInstance fiBeam)
		{
			try
			{
				Options options = this.m_Revit.Application.Application.Create.NewGeometryOptions();
                options.DetailLevel = (ViewDetailLevel)3;
				options.ComputeReferences = true;
				List<Solid> list = new List<Solid>();
				GeometryElement geometryElement = fiBeam.get_Geometry(options);
				if (null == geometryElement)
				{
					return 0.0;
				}
				List<GeometryObject> geometryObjectArray = RevitVersionFuncs.GetGeometryObjectArray(geometryElement);
				foreach (GeometryObject geometryObject in geometryObjectArray)
				{
					if (geometryObject.GetType() == typeof(Solid))
					{
						Solid solid = geometryObject as Solid;
						if (solid != null && Geometry.GreaterThan(solid.Volume, 0.0))
						{
							list.Add(solid);
							continue;
						}
					}
					GeometryInstance geometryInstance = geometryObject as GeometryInstance;
					if (!(null == geometryInstance))
					{
						GeometryElement instanceGeometry = geometryInstance.GetInstanceGeometry();
						List<GeometryObject> geometryObjectArray2 = RevitVersionFuncs.GetGeometryObjectArray(instanceGeometry);
						foreach (GeometryObject geometryObject2 in geometryObjectArray2)
						{
							Solid solid2 = geometryObject2 as Solid;
							if (solid2 != null && Geometry.GreaterThan(solid2.Volume, 0.0))
							{
								list.Add(solid2);
							}
						}
					}
				}
				foreach (Solid solid3 in list)
				{
					foreach (object obj in solid3.Faces)
					{
						Face face = (Face)obj;
						XYZ xyz = face.ComputeNormal(new UV(0.0, 0.0));
						if (xyz.IsAlmostEqualTo(XYZ.BasisZ))
						{
							try
							{
                                Edge edge = face.EdgeLoops.get_Item(0).get_Item(0);
								return edge.AsCurve().GetEndPoint(0).Z;
							}
							catch (Exception)
							{
							}
						}
					}
				}
			}
			catch (Exception)
			{
				return 0.0;
			}
			return 0.0;
		}

		private void JoinTwoElements(Element column, Element wall)
		{
			if (!JoinGeometryUtils.AreElementsJoined(this.m_Doc, column, wall))
			{
				JoinGeometryUtils.JoinGeometry(this.m_Doc, column, wall);
			}
			this.SetJoinOrder(column, wall);
		}

		private void SetJoinOrder(Element column, Element wall)
		{
			if (!JoinGeometryUtils.IsCuttingElementInJoin(this.m_Doc, column, wall))
			{
				JoinGeometryUtils.SwitchJoinOrder(this.m_Doc, column, wall);
			}
		}

		private ExternalCommandData m_Revit;

		private Document m_Doc;

		private double m_TopOffset = Common.MMToFeet(1500.0);
	}
}
